﻿using CleanArchitecture.Core;
using CleanArchitecture.Core.Configuration;
using CleanArchitecture.Core.Handlers;
using CleanArchitecture.Core.Helper;
using CleanArchitecture.Core.Http;
using CleanArchitecture.Core.Interfaces;
using CleanArchitecture.Infrastructure.AutoMapper;
using CleanArchitecture.Infrastructure.Entities.AdminUsers;
using CleanArchitecture.Infrastructure.Entities.Settings;
using CleanArchitecture.Infrastructure.Entities.SystemModules;
using CleanArchitecture.Infrastructure.Interfaces;
using CleanArchitecture.Infrastructure.Interfaces.AdminUsers;
using CleanArchitecture.Infrastructure.Interfaces.Common;
using CleanArchitecture.Infrastructure.Interfaces.SystemLog;
using CleanArchitecture.Infrastructure.Interfaces.SystemManage;
using CleanArchitecture.Infrastructure.Models;
using CleanArchitecture.Infrastructure.Models.AdminUsers;
using CleanArchitecture.Infrastructure.Plugins.BaiduAddressSDK;
using CleanArchitecture.Web.Backend.BackendApi;
using Microsoft.AspNetCore.Mvc.Rendering;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using static CleanArchitecture.Infrastructure.Entities.EntityEnumConfig;

namespace CleanArchitecture.Web.Factories
{
    public class AdminUserModelFactory : IAdminUserModelFactory
    {
        #region Field
        private readonly CaptchaSettings _captchaSettings;
        private readonly ISystemModuleService _systemModuleService;
        private readonly IWorkContext _workContext;
        private readonly IDateTimeHelper _datetimeHelper;
        private readonly NowaConfig _nowaConfig;
        private readonly ISystemLogService _logger;
        private readonly IGenericAttributeService _gaService;
        private readonly IAdminUserService _adminuserService;
        private readonly AdminAreaSettings _adminareaSettins;
        #endregion

        #region Ctor
        public AdminUserModelFactory(CaptchaSettings captchaSettings,
            ISystemModuleService systemModuleService,
            IWorkContext workContext,
            IDateTimeHelper datetimeHelper,
            NowaConfig nowaConfig,
            ISystemLogService logger,
            IGenericAttributeService gaService,
            IAdminUserService adminUserService,
            AdminAreaSettings adminareaSettings)
        {
            this._captchaSettings = captchaSettings;
            this._systemModuleService = systemModuleService;
            this._workContext = workContext;
            this._datetimeHelper = datetimeHelper;
            this._nowaConfig = nowaConfig;
            this._logger = logger;
            this._gaService = gaService;
            this._adminuserService = adminUserService;
            this._adminareaSettins = adminareaSettings;
        }
        #endregion
        /// <summary>
        /// Prepare the login model
        /// </summary>
        /// <returns>Login model</returns>
        public virtual LoginModel PrepareLoginModel()
        {
            var model = new LoginModel
            {
                RememberMe = true,
                CaptchaCode = string.Empty
            };
            return model;
        }
        /// <summary>
        /// 根据用户ID查询系统用户信息
        /// </summary>
        /// <param name="userId">if userid is 0: get Current LoginAdminUser</param>
        /// <returns></returns>
        public virtual AdminUserModel PrepareAdminUser(int userId = 0)
        {
            AdminUser CurrentUser = null;
            if (userId == 0) CurrentUser = _workContext.CurrentAdminUser;
            else CurrentUser = _adminuserService.GetAdminUserById(userId);
            if (CurrentUser == null) return null;
            var UserModel = CurrentUser.ToModel<AdminUserModel>();
            try
            {
                UserModel.LastLoginTime = _datetimeHelper.ConvertToUserTime(CurrentUser.LastLoginTimeUtc, DateTimeKind.Local);
                var availableRoles = _adminuserService.GetAdminRoleModelsAll();
                UserModel.AvailableAdminRoles = availableRoles.Select(role => new SelectListItem
                {
                    Text = role.RoleName,
                    Value = role.Id.ToString(),
                    Selected = CurrentUser.GetAdminRoles().Any(r => r.Id == role.Id)
                }).ToList();
                string defaultFaceImgPath = "0;/images/face.jpg";
                UserModel.FaceImageUrl = _gaService.GetAttribute<string>(CurrentUser, UserModel.nameof(m => m.FaceImageUrl), defaultFaceImgPath);
                //        CommonHelper.DefaultFileProvider.Combine(_hostingEnvironment.WebRootPath, defaultFaceImgPath));
                AdminUserPassword CurrentPassword = _adminuserService.GetCurrentPassword(UserModel.Id);
                UserModel.PasswordStrongLevel = GetAdminUserPasswordStrongLevel(CurrentPassword);
                IPAddress loginIp;
                if (!string.IsNullOrEmpty(CurrentUser.LastLoginIp) && IPAddress.TryParse(CurrentUser.LastLoginIp/*"182.242.186.95"*/, out loginIp))
                {
                    var client = EngineContext.Current.Resolve<IHttpClientFactory>().CreateClient(HttpDefineDefaults.DefaultHttpClient);
                    Task<BaiduIpAddress> GetIpTask = BaiduAddressSDK.GetIpLocationAsync(client, _nowaConfig, loginIp);
                    GetIpTask.Wait();
                    var baiduIpAddress = GetIpTask.Result;//await GetIpLocationAsync(loginIp);
                    if (baiduIpAddress == null) UserModel.LastLoginIpAddress = "localhost";
                    else UserModel.LastLoginIpAddress = baiduIpAddress.content.address;
                }
                return UserModel;
            }
            catch (NowaException ex)
            {
                _logger.Error(ex.Message);
                UserModel.LastLoginIpAddress = "";
                return UserModel;
            }
            catch (AggregateException ex) //For BaiduSdk not response exception
            {
                _logger.Error(ex.Message);
                UserModel.LastLoginIpAddress = "";
                return UserModel;
            }

        }
        /// <summary>
        /// 新增用户数据准备
        /// </summary>
        /// <returns></returns>
        public virtual AdminUserModel PrepareNewRegistedAdminUser()
        {
            AdminUserModel newUser = new AdminUserModel();
            var availableRoles = _adminuserService.GetAdminRoleModelsAll();
            newUser.AvailableAdminRoles = availableRoles.Select(role => new SelectListItem
            {
                Text = role.RoleName,
                Value = role.Id.ToString(),
                Selected = false
            }).ToList();
            string defaultFaceImgPath = "0;/images/face.jpg";
            newUser.FaceImageUrl = defaultFaceImgPath;
            newUser.IsActive = LockState.Active;
            return newUser;

        }

        private string GetAdminUserPasswordStrongLevel(AdminUserPassword password)
        {
            if (password == null) return "";
            switch (password.PasswordStrongLevel)
            {
                case PasswordStrongLevel.Weak:
                    return ApiResponseMessages.AccountPasswordStrongWeak;
                case PasswordStrongLevel.Medium:
                    return ApiResponseMessages.AccountPasswordStrongMedium;
                case PasswordStrongLevel.Strong:
                    return ApiResponseMessages.AccountPasswordStrongStrong;
                default:
                    return ApiResponseMessages.AccountPasswordStrongWeak;
            }
        }

        public virtual IList<NaviMenuNode> PrepareMenuTrees()
        {
            IList<SystemModule> recordLists = null;
            if (_workContext.IsAdmin) recordLists = _systemModuleService.GetSystemMenusAll();
            else
            {
                recordLists = _adminareaSettins.DisplayAllSystemMenus
                            ? _systemModuleService.GetSystemMenusAll()
                            : _systemModuleService.GetSystemMenuByUser(_workContext.CurrentAdminUser);
            }
            
            return PopulateMenu(recordLists.Where(r => r.ModuleType == SysModuleType.TopNaviMenu).ToList(), recordLists);

        }

        private IList<NaviMenuNode> PopulateMenu(IList<SystemModule> iterators, IList<SystemModule> menuRecords)
        {
            if (iterators == null || iterators.Count <= 0)
                return null;

            var nodes = new List<NaviMenuNode>();
            IList<NaviMenuNode> childrens = null;
            foreach (var record in iterators)
            {
                var menu = new NaviMenuNode()
                {
                    NodeId = record.Id,
                    NodeParentId = record.ParentId,
                    MenuType = (int)record.ModuleType,
                    Title = record.DisplayName,
                    IconCssClass = record.IconCssClass,
                    IconText = record.IconText,
                    Url = record.UrlRoute,
                    Target = record.OpenOnNewWindow == YesorNo.YES ? true : false
                };
                if (record.ModuleType == SysModuleType.ModulePage)
                {
                    menu.Spread = false;
                    menu.ChildNodes = null;
                }
                else
                {
                    menu.Spread = true;
                    childrens = PopulateMenu(menuRecords.Where(r => r.ParentId.Equals(record.Id)).ToList(), menuRecords);
                    /*** from https://stackoverflow.com/questions/100196/net-listt-concat-vs-addrange
                     *  AddRange modifies the list by adding the other items to it.
                     *  Concat returns a new sequence containing the list and the other items, without modifying the list.
                     ***/
                    //menu.ChildNodes.Concat(childrens.ToList());
                    if (childrens != null)
                        menu.ChildNodes = menu.ChildNodes.Concat(childrens).ToList();
                }
                nodes.Add(menu);
            }
            return nodes;
        }

        /// <summary>
        /// Prepare adminroles item list model
        /// </summary>
        /// <returns>AdminRoleModel list model</returns>
        //public virtual IList<AdminRoleModel> PrepareAdminRoleModels()
        //{
        //    var roleentities = _adminuserService.GetAdminRolesAll();
        //    //var organizes = _systemdatadicService.GetDataDicTreeByGroupType(DataDicGroup.Oragnize);
        //    Func<AdminRole, AdminRoleModel> SelectRoleModel = (entity) =>
        //    {
        //        var roleModel = entity.ToModel<AdminRoleModel>();
        //        if (entity.Organize != null)
        //            roleModel.OranizeName = entity.Organize.DisplayName; //organizes.FirstOrDefault(o => o.Id == entity.FKOrganizeId).DisplayName;
        //        return roleModel;
        //    };
        //    return roleentities.Select(entity => SelectRoleModel.Invoke(entity)).ToList();
        //}

        public AdminRoleModel PrepareAdminRoleModel(int roleId)
        {
            if (roleId <= 0) return null;
            var roleModels = _adminuserService.GetAdminRoleModelsAll();
            return roleModels.FirstOrDefault(r => r.Id == roleId);
        }

        public virtual AdminUserListModel PrepareAdminUserListModel(AdminUserSearchModel searchModel)
        {
            IViewPagedList<AdminUser> userItems = null;
            if (!string.IsNullOrEmpty(searchModel.SearchAccount))
            {
                AdminUser searchAccount = null;
                if (CommonHelper.IsValidMobile(searchModel.SearchAccount))
                    searchAccount = _adminuserService.GetAdminUserByMobile(searchModel.SearchAccount);
                else
                    searchAccount = _adminuserService.GetAdminUserByLoginname(searchModel.SearchAccount);
                if (searchAccount == null)
                    throw new NowaException(ApiResponseMessages.AdminUserNotExsitsError);
                userItems = new ViewPagedList<AdminUser>(new List<AdminUser> { searchAccount }, searchModel.Page - 1, searchModel.PageSize);
            }
            else
                userItems = _adminuserService.GetAdminUsers(searchModel.Page - 1, searchModel.PageSize);

            //prepare list model
            var models = new AdminUserListModel().PrepareToGrid(userItems, () =>
            {
                return userItems.Select(user =>
                {
                    //fill in model values from the entity
                    var userModel = user.ToModel<AdminUserModel>();
                    userModel.RoleNames = string.Join(", ", user.GetAdminRoles().Select(r => r.RoleName).ToList());
                    userModel.AvailableAdminRoles = null;
                    userModel.SelectedRoleIds = null;
                    string defaultFaceImgPath = "/images/face.jpg";
                    userModel.FaceImageUrl = _gaService.GetAttribute<string>(user, userModel.nameof(m => m.FaceImageUrl), defaultFaceImgPath);
                    userModel.IsAdmin = user.GetAdminRoles().Any(r => r.IsAdmin == YesorNo.YES)
                                      ? YesorNo.YES
                                      : YesorNo.No;
                    return userModel;
                });
            });
            return models;
        }
    }
}
